//go:build windows
// +build windows

package 浏览器类

import (
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/go_888/webview2/internal/edge"
	"gitee.com/go_888/webview2/internal/w32"
	"strconv"
	"strings"
	"sync"
	"unsafe"
)

var (
	窗口上下文 = sync.Map{}
)

func 取窗口上下文(wnd uintptr) interface{} {
	if v, ok := 窗口上下文.Load(wnd); ok {
		return v
	}
	return nil
}

func 设置窗口上下文(wnd uintptr, data interface{}) {
	窗口上下文.Store(wnd, data)
}

type 接口_浏览器 interface {
	Embed(hwnd uintptr) bool
	Resize()
	Navigate(url string)
	NavigateToString(htmlContent string)
	Init(script string)
	Eval(script string)
	NotifyParentWindowPositionChanged() error
	Focus()
	PrintToPDF(path string) error
	DisableContextMenu() error
	EnableContextMenu() error
	GetSettings() (*edge.ICoreWebViewSettings, error)
}

// 解析热键 将热键字符串解析为修饰符和键码。
// s: 热键字符串，例如 "ctrl+alt+s",大小写都可以
// 返回值:
// - 修饰符: 热键的修饰符，例如 ctrl、alt。
// - 键码: 热键的键码，例如 's'。
// - 错误: 如果热键字符串格式无效或包含未知的修饰符或键码，则返回错误。
func 解析热键(s string) (修饰符, 键码 int, 错误 error) {
	// 将热键字符串按 '+' 分割为各个部分。
	parts := strings.Split(s, "+")
	// 如果分割后的部分少于2个，说明热键格式无效。
	if len(parts) < 2 {
		return 0, 0, errors.New("热键格式无效")
	}

	// 初始化修饰符变量。
	var modifiers int
	// 提取最后一个部分作为键码，并转换为大写。
	key := strings.ToUpper(parts[len(parts)-1])

	// 解析修饰符
	for _, mod := range parts[:len(parts)-1] {
		// 根据修饰符的名称，设置对应的修饰符标志。
		switch strings.ToLower(strings.TrimSpace(mod)) {
		case "ctrl":
			modifiers |= w32.MOD_CONTROL
		case "alt":
			modifiers |= w32.MOD_ALT
		case "shift":
			modifiers |= w32.MOD_SHIFT
		case "win":
			modifiers |= w32.MOD_WIN
		default:
			// 如果遇到未知的修饰符，返回错误。
			return 0, 0, fmt.Errorf("未知的热键修饰符: %s", mod)
		}
	}

	// 解析键码
	var keyCode int
	// 如果键码部分长度为1，说明是字母或数字键。
	if len(key) == 1 {
		// 直接获取键码的ASCII值。
		keyCode = int(key[0])
	} else {
		// 如果键码部分长度大于1，说明是特殊键。
		// 根据特殊键的名称，设置对应的键码值。
		switch key {
		case "f1":
			keyCode = w32.VK_F1
		case "f2":
			keyCode = w32.VK_F2
		case "f3":
			keyCode = w32.VK_F3
		case "f4":
			keyCode = w32.VK_F4
		case "f5":
			keyCode = w32.VK_F5
		case "f6":
			keyCode = w32.VK_F6
		case "f7":
			keyCode = w32.VK_F7
		case "f8":
			keyCode = w32.VK_F8
		case "f9":
			keyCode = w32.VK_F9
		case "f10":
			keyCode = w32.VK_F10
		case "f11":
			keyCode = w32.VK_F11
		case "f12":
			keyCode = w32.VK_F12
		case "esc":
			keyCode = w32.VK_ESCAPE
		case "tab":
			keyCode = w32.VK_TAB
		case "space":
			keyCode = w32.VK_SPACE
		default:
			// 如果遇到未知的特殊键，返回错误。
			return 0, 0, fmt.Errorf("未知的热键键码: %s", key)
		}
	}

	// 返回解析出的修饰符、键码和nil错误，表示解析成功。
	return modifiers, keyCode, nil
}

// 热键生成唯一ID 根据给定的修饰符和键码生成一个唯一的ID。
// 该函数接受两个整数参数：修饰符和键码，并返回一个整数ID。
// 其中，修饰符代表键盘事件的修饰符（如Shift、Ctrl等），键码代表键盘的具体按键。
// 生成的唯一ID是通过将键码和修饰符转换为字符串，然后对这些字符串进行格式化处理，
// 最后将它们拼接在一起并转换回整数来实现的。
// 这个ID可以用来唯一地标识一个特定的键盘事件组合。
func 热键生成唯一ID(修饰符, 键码 int) (ID int) {
	// 将键码转换为字符串
	键码文本 := strconv.Itoa(键码)
	// 格式化键码，确保它至少有4位，不足部分在前面补0
	格式化键码 := fmt.Sprintf("%s%0*d", 键码文本, 4-len(键码文本), 0)
	// 如果格式化后的键码是'0000'，则返回0
	if 格式化键码 == "0000" {
		return 0
	}

	// 将修饰符转换为字符串
	修饰符文本 := strconv.Itoa(修饰符)
	// 格式化修饰符，确保它至少有4位，不足部分在前面补0
	格式化修饰符 := fmt.Sprintf("%s%0*d", 修饰符文本, 4-len(修饰符文本), 0)
	// 将键码文本和格式化后的修饰符文本拼接在一起，并转换为整数
	返回id, _ := strconv.Atoi(键码文本 + 格式化修饰符)
	// 返回拼接并转换后的整数作为唯一ID
	return 返回id
}
func jsString(v interface{}) string { b, _ := json.Marshal(v); return string(b) }
func 处理窗口过程(窗口句柄, 消息, wp, lp uintptr) uintptr {
	if w, ok := 取窗口上下文(窗口句柄).(*Web视图); ok {
		switch 消息 {
		case w32.X常量_移动, w32.X常量_移动中:
			_ = w.浏览器.NotifyParentWindowPositionChanged()
		case w32.X常量_左键按下:
			if wp == w32.HTCaption {
				// 直接调用 DefWindowProc 处理拖动
				r, _, _ := w32.X默认窗口过程W.Call(窗口句柄, 消息, wp, lp)
				return r
			}
			_, _, _ = w32.X设置焦点.Call(w.窗口句柄)
			r, _, _ := w32.X默认窗口过程W.Call(窗口句柄, 消息, wp, lp)
			return r
		case w32.X常量_调整大小:
			w.浏览器.Resize()
		case w32.X常量_激活:
			if wp == w32.WAInactive {
				break
			}
			if w.是否自动聚焦 {
				w.浏览器.Focus()
			}
		case w32.X常量_销毁:
		//	_, _, _ = w32.X发送退出消息.Call(0)
		case w32.X常量_关闭:
			//当调用 DestroyWindow 时，它会发送以下消息给窗口过程：
			//WM_DESTROY (0x0002)：表示窗口正在被销毁。
			//WM_NCDESTROY (0x0082)：表示非客户区（non-client area）正在被销毁。这是在 WM_DESTROY 之后发送的。
			//WM_CLOSE (0x0010)：如果窗口没有子窗口或所有子窗口已经关闭，则会发送此消息。
			if w.事件_关闭窗口 != nil {
				if w.事件_关闭窗口() {
					_, _, _ = w32.X销毁窗口.Call(窗口句柄)
					窗口上下文.Delete(w.窗口句柄)
				}
			} else {
				_, _, _ = w32.X销毁窗口.Call(窗口句柄)
				窗口上下文.Delete(w.窗口句柄)
			}

			// 判断是否为最后一个窗口.如果是最后一个窗口,就发送退出消息.
			计次 := 0
			窗口上下文.Range(func(key, value interface{}) bool {
				计次++
				return true
			})
			if 计次 == 0 {
				_, _, _ = w32.X发送退出消息.Call(0)
			}

		case w32.X常量_获取最小最大化信息:
			lpmmi := (*w32.MinMaxInfo)(unsafe.Pointer(lp))
			if w.最大尺寸.X > 0 && w.最大尺寸.Y > 0 {
				lpmmi.PtMaxSize = w.最大尺寸
				lpmmi.PtMaxTrackSize = w.最大尺寸
			}
			if w.最小尺寸.X > 0 && w.最小尺寸.Y > 0 {
				lpmmi.PtMinTrackSize = w.最小尺寸
			}

		case w32.WMLButtonDown:
			if wp == w32.HTCaption {
				_, _, _ = w32.X发送消息W.Call(窗口句柄, w32.X常量_左键按下, wp, lp)
				return 0
			}
		case w32.WMHotKey:
			w.锁.Lock()
			if handler, ok := w.热键map[int(wp)]; ok {
				w.锁.Unlock()
				handler()
			} else {
				w.锁.Unlock()
			}
			return 0
		default:
			r, _, _ := w32.X默认窗口过程W.Call(窗口句柄, 消息, wp, lp)
			return r
		}
		return 0
	}
	r, _, _ := w32.X默认窗口过程W.Call(窗口句柄, 消息, wp, lp)
	return r
}
